#include "stdafx.h"
#include "VariableWatch.h"

LUA_DEBUG_BEGIN

// Alive instance set.
VariableWatch::InstanceSet VariableWatch::ms_aliveInstanceSet;

BEGIN_EVENT_TABLE(VariableWatch, wxTreeListCtrl)
EVT_SIZE(VariableWatch::OnSize)
EVT_TREE_ITEM_EXPANDED(wxID_ANY, VariableWatch::OnExpanded)
EVT_TREE_END_LABEL_EDIT(wxID_ANY, VariableWatch::OnEndLabelEdit)
EVT_LIST_COL_END_DRAG(wxID_ANY, VariableWatch::OnColEndDrag)
EVT_DEBUG_END_DEBUG(wxID_ANY, VariableWatch::OnEndDebug)
END_EVENT_TABLE()

const long style =
wxTR_HAS_BUTTONS | wxTR_LINES_AT_ROOT |
wxTR_HIDE_ROOT | 
wxTR_EDIT_LABELS |
wxTR_ROW_LINES |
//wxTR_COL_LINES |
wxTR_FULL_ROW_HIGHLIGHT |
wxALWAYS_SHOW_SB;

VariableWatch::VariableWatch(Mediator_ptr mediator,
                             wxWindow *parent,
                             int id, 
                             bool isShowColumn,
                             bool isShowType,
                             bool isLabelEditable,
                             bool isEvalLabels, 
                             const VarListRequester &requester) :
wxTreeListCtrl(parent, id, wxDefaultPosition, wxDefaultSize, style),
m_mediator(mediator),
m_isLabelEditable(isLabelEditable),
m_isEvalLabels(isEvalLabels),
m_requester(requester)
{
    if (true) {
        // Set the header font.
        wxFont font(GetFont());
        font.SetPointSize(9);
        SetFont(font);

        AddColumn(_("Name"), 80, wxALIGN_LEFT, -1, true, true);
        AddColumn(_("Value"), 120, wxALIGN_LEFT, -1, true, true);
        if (isShowType) {
            AddColumn(_("Type"), 60, wxALIGN_LEFT, -1, true, true);
        }
        SetLineSpacing(2);
    }

    AddRoot(wxT(""), -1, -1, new VariableWatchItemData(m_mediator,LuaVar()));
    if (isLabelEditable) {
        AppendItem(GetRootItem(), wxT("                                "),
            -1, -1, new VariableWatchItemData(m_mediator,LuaVar()));
    }

    ms_aliveInstanceSet.insert(this);
}

VariableWatch::~VariableWatch()
{
    ms_aliveInstanceSet.erase(this);
}

wxTreeItemIdList VariableWatch::GetItemChildren(const wxTreeItemId &item)
{
    wxTreeItemIdList result;
    wxTreeItemIdValue cookie;

    for (wxTreeItemId child = GetFirstChild(item, cookie);
        child.IsOk();
        child = GetNextChild(item, cookie)) {
            result.push_back(child);
    }

    return result;
}

VariableWatchItemData * VariableWatch::GetItemData(const wxTreeItemId &item)
{
    return static_cast<VariableWatchItemData *>(wxTreeListCtrl::GetItemData(item));
}

void VariableWatch::BeginUpdating(wxTreeItemId item, bool isExpanded, const VarListRequester &request)
{
    VariableWatchItemData *data = GetItemData(item);
    if (data == NULL) {
        return;
    }

    // Does this need to request newbies ?
    if (data->GetRequestCount() < m_mediator->GetUpdateCount()) {
        RequestVarListCallback callback(m_mediator,this, item, isExpanded);
        request(callback);
        data->Requested();
    }
    else if (isExpanded) {
        wxTreeItemIdList children = GetItemChildren(item);

        wxTreeItemIdList::iterator it;
        for (it = children.begin(); it != children.end(); ++it) {
            VariableWatchItemData *data = GetItemData(*it);
            if (data != NULL) {
                BeginUpdating(*it, false, data->GetVar());
            }
        }
    }
}

void VariableWatch::BeginUpdating( wxTreeItemId item, bool isExpanded, const LuaVar &var )
{
    BeginUpdating(item, isExpanded, FieldsRequester(m_mediator,var));
}

void VariableWatch::BeginUpdating()
{
    if (m_isLabelEditable) {
        BeginUpdating(GetRootItem(), true, EvalLabelRequester(m_mediator,this));
    }
    else {
        BeginUpdating(GetRootItem(), true, m_requester);
    }
}

void VariableWatch::Clear()
{
    DeleteRoot();
    AddRoot(wxT(""), -1, -1, new VariableWatchItemData(m_mediator,LuaVar()));
}

void VariableWatch::DoUpdateVars( wxTreeItemId parent, const LuaVarList &vars, bool isExpand )
{
    VariableWatchItemData *parentData = GetItemData(parent);
    if (parentData->GetUpdateCount() == m_mediator->GetUpdateCount()) {
        return;
    }

    // The current chilren list.
    // If the item is updated, it is removed.
    wxTreeItemIdList children = GetItemChildren(parent);
    bool isEvalLabels = (m_isEvalLabels && parent == GetRootItem());

    // If each tree's label were evaluted...
    if (isEvalLabels) {
        wxASSERT(children.size() == vars.size());
    }

    for (LuaVarList::size_type i = 0; i < vars.size(); ++i) {
        const LuaVar &var = vars[i];
        wxTreeItemIdList::iterator it;
        wxTreeItemId item;

        // Set item to 'it'.
        if (isEvalLabels) {
            // Because the item of 'children' is eliminated.
            it = children.begin();
        } else {
            // Find the item that has the same LuaVar object.
            it = std::find_if(children.begin(), children.end(), ItemComparator(this, var));
        }

        // Does the item exist ?
        if (it == children.end()) { // No!
            item = AppendItem(parent, wxEmptyString, -1, -1, new VariableWatchItemData(m_mediator,var));

            //wxString name = wxConvFromCtxEnc(var.GetName());
            wxString name = var.GetName();
            SetItemText(item, 0, name);
        }
        else {
            // Use the exist item.
            item = *it;
            children.erase(it);

            // Replace the item data.
            VariableWatchItemData *oldData = GetItemData(item);
            if (oldData != NULL) {
                delete oldData;
            }
            SetItemData(item, new VariableWatchItemData(m_mediator,var));
        }

        // To avoid the useless refresh, check change of the title.
        //wxString value = wxConvFromCtxEnc(var.GetValue());
        wxString value = fuStringHelper(var.GetValue()).Utf8ToAnsi();
        if (GetItemText(item, 1) != value) {
            SetItemText(item, 1, value);
        }

        // Check whether it has the type column.
        if (GetColumnCount() >= 3) {
            //wxString type = wxConvFromCtxEnc(var.GetValueTypeName());
            wxString type = fuStringHelper(var.GetValueTypeName()).Utf8ToAnsi();
            if (GetItemText(item, 2) != type) {
                SetItemText(item, 2, type);
            }
        }

        // Refresh the chilren, too.
        if (var.HasFields()) {
            if (!HasChildren(item)) {
                // Item for lazy evalution
                AppendItem(item, _T(""));
            }

            if (IsExpanded(item)) {
                BeginUpdating(item, true, var);
            }
            else if (isExpand) {
                BeginUpdating(item, false, var);
            }
        }
        else {
            if (HasChildren(item)) {
                // The state whether the item is expanded or collapsed
                // has been saved, so collapse it carefully.
                if (IsExpanded(item)) {
                    //Collapse(item);
                }

                // Delete all child items.
                DeleteChildren(item);
            }

            // Update was done.
            VariableWatchItemData *data = GetItemData(item);
            data->Updated();
        }
    }

    // Remove all items that were not refreshed or appended.
    wxTreeItemIdList::iterator it;
    for (it = children.begin(); it != children.end(); ++it) {
        Delete(*it);
    }

    // Update was done.
    parentData->Updated();
}

void VariableWatch::OnExpanded( wxTreeEvent &event )
{
    event.Skip();

    VariableWatchItemData *data = GetItemData(event.GetItem());
    BeginUpdating(event.GetItem(), true, data->GetVar());
}

void VariableWatch::OnEndLabelEdit( wxTreeEvent &event )
{
    event.Skip();

    if (m_isLabelEditable) {
        // The last label isn't only the white space,
        // add a new label.
        wxTreeItemIdValue cookie;
        wxTreeItemId item = GetLastChild(GetRootItem(), cookie);
        if (item == event.GetItem()) {
            wxString label = event.GetLabel();
            if (!label.Strip(wxString::both).IsEmpty()) {
                AppendItem(GetRootItem(), wxT("                     "),
                    -1, -1, new VariableWatchItemData(m_mediator,LuaVar()));
            }
        }

        // If it is changed, we update the value of the var.
        if (GetItemText(event.GetItem()) != event.GetLabel()) {
            SetItemText(event.GetItem(), event.GetLabel());
            m_mediator->IncUpdateCount();
            BeginUpdating();
        }
    }
    else {
        event.Veto();
    }
}

int VariableWatch::GetContentWidth()
{
    return ( GetClientSize().GetWidth() - wxSystemSettings::GetMetric(wxSYS_VSCROLL_X));
}

void VariableWatch::LayoutColumn( int targetColumn )
{
    if (GetColumnCount() <= 1) {
        if (GetColumnCount() == 1) {
            SetColumnWidth(0, GetClientSize().GetWidth());
        }
        return;
    }

    int resizeItem = targetColumn + 1;
    if (resizeItem == GetColumnCount()) {
        resizeItem = GetColumnCount() - 1;
    }

    int columnsWidth = 0; // column's width
    for (int i = 0; i < GetColumnCount(); ++i) {
        if (i != resizeItem) {
            columnsWidth += GetColumnWidth(i);
        }
    }

    // Do resize the column.
    SetColumnWidth(
        resizeItem,
        GetContentWidth() - columnsWidth);
}

void VariableWatch::FitColumns()
{
    int curWidth = 0; // column's size
    for (int i = 0; i < GetColumnCount(); ++i) {
        curWidth += GetColumnWidth(i); 
    }

    // prevWidth is the currently resizable width.
    int width = GetContentWidth();
    double ratio = (double)width / curWidth;
    if (ratio < 0.001) {
        return;
    }

    // Resize the columns with the same ratio.
    for (int i = 0; i < GetColumnCount(); ++i) {
        int w = GetColumnWidth(i);
        SetColumnWidth(i, (int)(w * ratio));
    }
}

void VariableWatch::OnSize( wxSizeEvent &event )
{
    event.Skip();
    FitColumns();
}

void VariableWatch::OnColEndDrag( wxListEvent &event )
{
    event.Skip();
    LayoutColumn(event.GetColumn());
}

void VariableWatch::OnEndDebug( wxDebugEvent &event )
{
    event.Skip();
    Clear();
}

LUA_DEBUG_END
